"""提供公告板文件相关功能的服务模块."""
import os
import shutil
from typing import List
from fastapi import Request, UploadFile
from sqlalchemy import exists, func, insert, select

from core.database import db_session
from core.models import Board, BoardFile


class BoardFileService():
    """
   提供公告栏文件相关服务的相关性注入类。
    """
    def __init__(self, request: Request, db: db_session):
        self.request = request
        self.config = request.state.config
        self.db = db

    def is_exist(self, bo_table: str, wr_id: int):
        """检查帖子中是否有文件

        Returns:
        bool:如果文件存在True，则为False
        """
        return self.db.scalar(
            exists(BoardFile)
            .where(BoardFile.bo_table == bo_table, BoardFile.wr_id == wr_id)
            .select()
        )

    def is_upload_extension(self, file: UploadFile) -> bool:
        """
        确认上传文件扩展名。

Args:
file（UploadFile）：上载文件

Returns:
bool：如果文件扩展名可以上传，则为True或False
        """
        ext = file.filename.split(".")[-1]
        content = file.content_type

        if (("image" in content and not ext in self.config.cf_image_extension)
                or ("x-shockwave-flash" in content and not ext in self.config.cf_flash_extension)
                or (("audio" in content or "video" in content) and not ext in self.config.cf_movie_extension)):
            return False

        return True

    def is_upload_size(self, board: Board, file: UploadFile) -> bool:
        """确认上传文件的大小。

Args:
file（UploadFile）：上载文件

Returns:
bool:上传文件大小小于设置的值为True，大于为False
        """
        if file.size <= 0:
            return False

        if not board.bo_upload_size:
            return True

        return file.size <= board.bo_upload_size

    def get_board_files(self, bo_table: str, wr_id: int) -> List[BoardFile]:
        """导入上传的文件列表。"""
        return self.db.scalars(
            select(BoardFile).where(
                BoardFile.bo_table == bo_table,
                BoardFile.wr_id == wr_id
            )
        ).all()

    def get_board_files_by_form(self, board: Board, wr_id: int = None) -> List[BoardFile]:
        """导入要在输入/修改表单中使用的文件列表。

        Returns:
            list[BoardFile]: 上传文件列表 
        """
        config_count = int(board.bo_upload_count) or 0
        if wr_id:
            query = select().where(BoardFile.bo_table == board.bo_table, BoardFile.wr_id == wr_id)
            uploaded_count = self.db.scalar(query.add_columns(func.count()).select_from(BoardFile).order_by(None))
            uploaded_files = self.db.scalars(query.add_columns(BoardFile)).all()
            # 文件计数设置为上传的文件数和设置的值中的大数。
            upload_count = (uploaded_count if uploaded_count > config_count else config_count) - uploaded_count
        else:
            uploaded_files = []
            upload_count = config_count

        # 上载文件+空对象
        files = uploaded_files + [BoardFile() for _ in range(upload_count)] #为何这里要加入空的对象；

        return files

    def get_board_files_by_type(self, bo_table: str, wr_id: int):
        """将上传的文件列表分离为文件和图像。

        Args:
            request (Request): Request 对象

        Returns:
            list[BoardFile]: 文件列表
            list[BoardFile]: 图像列表
        """
        board_files = self.get_board_files(bo_table, wr_id)
        images = []
        files = []
        for file in board_files:
            ext = file.bf_source.split('.')[-1]
            if ext in self.config.cf_image_extension:
                images.append(file)
            else:
                files.append(file)

        return images, files

    def get_board_file(self, bo_table: str, wr_id: int, bf_no: int):
        """导入上传的文件。

        Args:
            bf_no (int): 文件顺序

        Returns:
            BoardFile: 文件上传
        """
        return self.db.get(BoardFile, {"bo_table": bo_table, "wr_id": wr_id, "bf_no": bf_no})

    def get_filename(self, filename: str):
        """生成文件名。

        Args:
            filename (str): 上传文件名

        Returns:
            str: 文件名
        """
        return os.urandom(16).hex() + "." + filename.split(".")[-1]

    def insert_board_file(self, bo_table: str, wr_id: int, bf_no: int, directory: str, filename: str, file: UploadFile, content: str = ""):
        """添加帖子的文件。

        Args:
            bf_no (int): 文件顺序
            directory (str): 文件存储路径
            file (UploadFile): 上传文件
            content (str, optional): 文件说明. Defaults to "".
            bo_table (str, optional): 公告板表名. Defaults to None.
            wr_id (int, optional): 帖子名称. Defaults to None.
        """
        self.db.execute(
            insert(BoardFile)
            .values(
                bo_table=bo_table,
                wr_id=wr_id,
                bf_no=bf_no,
                bf_source=file.filename,
                bf_file=f"{directory}/{filename}",
                bf_download=0,
                bf_content=content,
                bf_filesize=file.size
            )
        )
        self.db.commit()

    def update_board_file(self, board_file: BoardFile,
                          directory: str, filename: str, file: UploadFile,
                          content: str = "", bo_table: str = None, wr_id: int = None):
        """修改帖子的文件。

        Args:
            board_file (BoardFile): 公告板文件即时
            directory (str): 文件存储路径
            file (UploadFile): 上传文件
            content (str, optional): 文件说明. Defaults to "".
        """
        if bo_table:
            board_file.bo_table = bo_table
        if wr_id:
            board_file.wr_id = wr_id
        board_file.bf_source = file.filename
        board_file.bf_file = f"{directory}/{filename}"
        board_file.bf_download = 0
        board_file.bf_content = content
        board_file.bf_filesize = file.size
        self.db.commit()

    def update_download_count(self, board_file: BoardFile):
        """增加下载次数。

        Args:
            board_file (BoardFile): 公告板文件即时
        """
        board_file.bf_download += 1
        self.db.commit()

    def move_board_files(self, directory: str,
                         origin_bo_table: str, origin_wr_id: int,
                         target_bo_table: str, target_wr_id: int):
        """移动帖子中的文件。

        Args:
            target_bo_table (str): 要移动的公告板表名称
            target_wr_id (int): 发布要移动的想法
        """
        board_directory = os.path.join(directory, target_bo_table)
        os.makedirs(board_directory, exist_ok=True)

        board_files = self.get_board_files(origin_bo_table, origin_wr_id)
        for board_file in board_files:
            file = self.create_upload_file_from_path(board_file.bf_file)
            file.filename = board_file.bf_source
            file.size = board_file.bf_filesize
            filename = self.get_filename(file.filename)

            # 移动文件和更新信息
            self.move_file(board_file.bf_file, f"{directory}/{filename}")
            self.update_board_file(board_file, directory, filename, file,
                                   board_file.bf_content, target_bo_table, target_wr_id)
            board_file.bo_table = target_bo_table
            board_file.wr_id = target_wr_id

        self.db.commit()

    def copy_board_files(self, directory: str,
                         origin_bo_table: str, origin_wr_id: int,
                         target_bo_table: str, target_wr_id: int) -> None:
        """我们复制帖子的文件."""
        board_directory = os.path.join(directory, target_bo_table)
        os.makedirs(board_directory, exist_ok=True)

        board_files = self.get_board_files(origin_bo_table, origin_wr_id)
        for board_file in board_files:
            file = self.create_upload_file_from_path(board_file.bf_file)
            if not file:
                continue
            file.filename = board_file.bf_source
            file.size = board_file.bf_filesize
            filename = self.get_filename(file.filename)

            # 文件复制和添加信息
            self.copy_file(board_file.bf_file, f"{board_directory}/{filename}")
            self.insert_board_file(target_bo_table, target_wr_id, board_file.bf_no,
                                   board_directory, filename, file, board_file.bf_content)

    def delete_board_file(self, bo_table: str, wr_id: int, bf_no: int):
        """删除帖子的文件."""
        board_file = self.get_board_file(bo_table, wr_id, bf_no)
        if not board_file:
            return
        self.remove_file(board_file.bf_file)
        self.db.delete(board_file)
        self.db.commit()

    def delete_board_files(self, bo_table: str, wr_id: int):
        """删除帖子中的所有文件."""
        board_files = self.get_board_files(bo_table, wr_id)
        for board_file in board_files:
            # 删除文件
            self.remove_file(board_file.bf_file)
            # 删除以相同路径中的文件名结尾的文件
            directory = os.path.dirname(board_file.bf_file)
            filename = os.path.basename(board_file.bf_file)
            for file in os.listdir(directory):
                if file.endswith(filename):
                    self.remove_file(os.path.join(directory, file))
            # 删除文件信息
            self.db.delete(board_file)
        self.db.commit()

    def upload_file(self, directory: str, filename: str, file: UploadFile):
        """我们上传一个文件.

        Args:
            directory (str): 文件存储路径
            filename (str): 文件名
            file (UploadFile): 上传文件
        """
        if file and file.filename:
            with open(f"{directory}/{filename}", "wb") as buffer:
                shutil.copyfileobj(file.file, buffer)

    def move_file(self, origin: str, target: str):
        """移动文件.

        Args:
            origin (str): 原始文件路径
            target (str): 要移动的文件路径
        """
        if os.path.exists(origin):
            shutil.move(origin, target)

    def copy_file(self, origin: str, target: str):
        """我们复制文件。.

        Args:
            origin (str): 原始文件路径
            target (str): 要复制的文件路径
        """
        if os.path.exists(origin):
            shutil.copy(origin, target)

    def remove_file(self, path: str):
        """删除文件.

        Args:
            path (str): 文件路径
        """
        if os.path.exists(path):
            os.remove(path)

    def create_upload_file_from_path(self, path: str):
        """通过文件路径创建UploadFile对象。

        Args:
            path (str): 文件路径

        Returns:
            UploadFile:上载文件
        """
        if os.path.exists(path):
            with open(path, "rb") as f:
                return UploadFile(f, filename=os.path.basename(path))
